package com.googlecode.q2xml.service;

import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import com.googlecode.q2xml.model.ByteCodeUtil;
import com.googlecode.q2xml.model.MSClassLoader;
import com.googlecode.q2xml.model.asm.BeanGenerator;
import com.googlecode.q2xml.service.jdbc.ASMJdbcTypeMapper;
import com.googlecode.q2xml.service.jdbc.Column;
import com.googlecode.q2xml.util.Constants;
import com.googlecode.q2xml.util.SqlQueryHelper;

public class IndexerServiceImpl extends BaseServiceImpl implements IndexerService 
{
	private Logger logger = Logger.getLogger(this.getClass().getSimpleName());
	
	public IndexerServiceImpl()
	{
	}
	
	byte[] getBytecode(List<Column> columns)
	{
		BeanGenerator generator = new BeanGenerator();
		byte[] bytecode = generator.generate(Constants.BASE_GEN_PACKAGE_NAME + "/" +Constants.CLASS_NAME, columns);
		return bytecode;
	}
	
	List<Column> getColumns(ResultSetMetaData metaData) throws SQLException
	{
		List<Column> columns = new ArrayList<Column>();
		
		int colsCount = metaData.getColumnCount();
		
		for (int i=1; i<=colsCount; i++)
		{
			String colName= metaData.getColumnLabel(i);
			// TODO
//			if ("true".equalsIgnoreCase(PropertiesReader.get("use_column_alias")))
//				colName = metaData.getColumnLabel(i);
//			else
//				colName = metaData.getColumnName(i);
			columns.add(new Column(colName, ASMJdbcTypeMapper.getType(metaData.getColumnType(i))));
		}
		
		return columns;
	}
	
	List populateObject(Class c, ResultSet resultSet) throws Exception
	{
		List ret = new ArrayList();
		
		ResultSetMetaData metaData = resultSet.getMetaData();
		int count = metaData.getColumnCount();
		
		Field[] fs = c.getFields();
		
		int rowCount = 0;
		while (resultSet.next())	// next row
		{
			rowCount++;
			Object o = c.newInstance();
			
			for (int i=0; i<count; i++)
			{
				int rsIndex = i+1;
				Object value;
				// TODO extend this to handle any type of object returned
				if (isNumbericField(metaData.getColumnType(rsIndex)))
				{
					value = resultSet.getLong(rsIndex);
				}else if(isFloatField(metaData.getColumnType(rsIndex)))
				{
					value = resultSet.getDouble(rsIndex);
				}else if (isDateField(metaData.getColumnType(rsIndex)))
				{
					value = resultSet.getDate(rsIndex);
				}else
				{
					value = resultSet.getString(rsIndex);
				}
				fs[i].set(o, value);
			}
			ret.add(o);
		}
		return ret;
	}
	
	boolean isNumbericField(int rsIndex)
	{
		return rsIndex == Types.INTEGER || rsIndex == Types.BIGINT || rsIndex == Types.NUMERIC || rsIndex == Types.SMALLINT;
	}
	
	boolean isFloatField(int rsIndex)
	{
		return rsIndex == Types.DECIMAL || rsIndex == Types.DOUBLE || rsIndex == Types.FLOAT || rsIndex == Types.REAL;
	}
	
	boolean isDateField(int rsIndex)
	{
		return rsIndex == Types.DATE || rsIndex == Types.TIME || rsIndex == Types.TIMESTAMP;
	}
	
	@SuppressWarnings("rawtypes")
	public List getSearchResult()
	{
		Statement stmt = null;
		List ret = new ArrayList();
		try
		{
			String sql = SqlQueryHelper.getSQLQuery(); 
			logger.info(sql);
			
			stmt = managedConn.createStatement();
			ResultSet resultSet = stmt.executeQuery(sql);
			
			ResultSetMetaData metaData = resultSet.getMetaData();
			List<Column> columns = getColumns(metaData);
			byte[] bytecode = getBytecode(columns);
			
			Class c = new MSClassLoader().defineClass(ByteCodeUtil.getByteCodeBinaryClassName(), bytecode);
			
			ret = populateObject(c, resultSet);

		} catch (Exception e)
		{
			e.printStackTrace();
			logger.severe(e.getMessage());
		}finally
		{
			if (stmt != null)
				try {stmt.close();}catch(Exception ex) {}
		}

		return ret;
	}
}
